//
// Copyright (c) 2005 BroadVision, Inc. All rights reserved.
//
// This software is copyrighted.  Under the copyright laws, this software
// may not be copied, in whole or in part, without prior written consent
// of BroadVision, Inc. or its assignees.  This software is provided under
// the terms of a license between BroadVision and the recipient, and its
// use is subject to the terms of that license.
//
// This software may be protected by one or more U.S. and International
// patents.  Certain applications of BroadVision One-To-One software are
// covered by U.S. patent 5,710,887.
//
// TRADEMARKS: BroadVision and BroadVision One-To-One are registered
// trademarks of BroadVision, Inc., in the United States and the European
// Community, and are trademarks of BroadVision, Inc., in other
// countries.  The BroadVision logo, is a trademark of BroadVision, Inc.,
// in the United States and other countries. Additionally, IONA and Orbix
// are trademarks of IONA Technologies, Ltd.  RSA, MD5, and RC2 are
// trademarks of RSA Data Security, Inc.
//

package com.broadvision.manage.common.tools.action.user;

import java.util.*;
import java.security.*;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;

import org.apache.struts.util.MessageResources;
import org.apache.struts.action.ActionForm;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.struts.Globals;

import com.broadvision.system.accesscontrol.client.*;
import com.broadvision.system.delegation.*;
import com.broadvision.data.common.TableData;
import com.broadvision.data.client.DataManager;
import com.broadvision.ssp.util.*;
import com.broadvision.ssp.client.SelfServiceProcess;
import com.broadvision.ssp.client.workflow.*;

import com.broadvision.visitor.exceptions.*;
import com.broadvision.visitor.client.VisitorManager;
import com.broadvision.visitor.client.Visitor;

import com.broadvision.web.servlet.SessionVisitor;
import com.broadvision.web.util.ViewDynaBean;
import com.broadvision.web.util.DateTime;
import com.broadvision.web.action.Tab;
import com.broadvision.portal.services.util.TransferBeanUtils;
import com.broadvision.ssp.util.SspLog;
import com.broadvision.manage.common.tools.user.*;
import com.broadvision.web.util.ViewDynaBean;


/**
 * Utility class for user management
 */
public class UserUtils {

  /**
   * The key in property file. This key's value has only first name, middle
   * name, and last name. Space is optional. There is no any other letter, 
   * such as ",".
   */
  public static final String FULL_NAME_ONLY_KEY = "fullNameOnly";

  private static final String FIRST_NAME  = "FIRST_NAME";
  private static final String MIDDLE_NAME = "MIDDLE_NAME";
  private static final String LAST_NAME   = "LAST_NAME";

  private static final String NAME = "NAME";
  private static DataManager dataManager = null;

  public static String getFormattedName(long userId,
					MessageResources resources,
					Locale locale)
    throws Exception
  {
    UserProfile visitorProfile = new UserProfile(userId);
    visitorProfile.view();
    return getFormattedName(visitorProfile.getVisitor(), resources, locale);
  }

  public static String getFormattedName(Visitor visitor,
					MessageResources resources,
					Locale locale)
    throws Exception
  {
    return getFormattedVisitorFullName(visitor, resources, locale, FULL_NAME_ONLY_KEY);
  }

  public static String getFormattedVisitorFullName (Visitor visitor, 
                                                    MessageResources resources, 
                                                    Locale locale, 
                                                    String messageKey)
  {
    if ( VisitorManager.isUsingFMLName()) {
      final String firstName = visitor.getStringValue(FIRST_NAME);
      final String middleName = visitor.getStringValue(MIDDLE_NAME);
      final String lastName = visitor.getStringValue(LAST_NAME);
      String fullName = resources.getMessage(locale, messageKey, firstName, middleName, lastName);
      
      int index = fullName.indexOf("  ");
      if (index != -1) {
	  StringBuffer tmp = new StringBuffer(fullName);
	  tmp.deleteCharAt(index);
	  fullName =  tmp.toString();
      }
      return fullName;    
    }
    else {
      return visitor.getStringValue (NAME); 
    }
  }

  public static String getFullName(Long userId,
				   MessageResources resources,
				   Locale locale)
    throws Exception
  {
    return getFormattedName(userId.longValue(), resources, locale);
  }

  public static boolean isProcessManager(Visitor visitor, int serviceId) 
    throws Exception
  {

    boolean isProcessMgr = isManagerForAnyProcessModel(visitor.getUserAlias(), serviceId);

    if (isProcessMgr)
      return true;

    isProcessMgr = isManagerForAnyProcessInstance(visitor.getUserAlias(), serviceId);

    if (isProcessMgr)
      return true;

    // check whether the user has been delegated the role
    // We assume that only those users who currently have ProcessManager 
    // role can  do "ProcessManager" delegation. Then we only need to 
    // check "Is there any people delegate "ProcessManager" responsibility
    // to the visitor?"
    DelegationManager dm = new DelegationManager();
    long[] delegators = dm.getDelegators(visitor.getId(),
					 new Date(), SelfServiceProcess.DELEGATION_TYPE);
    
    VisitorManager visitorMgr = new VisitorManager();
    for (int i = 0; i< delegators.length; i++) {
      Visitor delegatorVisitor = visitorMgr.getVisitor(delegators[i]);
      if (isProcessManager(delegatorVisitor, serviceId)) {
	return true;
      }
    }     

    return false;
  }

  public static boolean isProcessManagerOnly(Visitor visitor, int serviceId) 
    throws Exception
  {

    boolean isProcessMgr = isManagerForAnyProcessModel(visitor.getUserAlias(), serviceId);

    if (isProcessMgr)
      return true;

    // check whether the user has been delegated the role
    // We assume that only those users who currently have ProcessManager 
    // role can  do "ProcessManager" delegation. Then we only need to 
    // check "Is there any people delegate "ProcessManager" responsibility
    // to the visitor?"
    DelegationManager dm = new DelegationManager();
    long[] delegators = dm.getDelegators(visitor.getId(),
					 new Date(), SelfServiceProcess.DELEGATION_TYPE);
    
    VisitorManager visitorMgr = new VisitorManager();
    for (int i = 0; i< delegators.length; i++) {
      Visitor delegatorVisitor = visitorMgr.getVisitor(delegators[i]);
      if (isProcessManagerOnly(delegatorVisitor, serviceId)) {
	return true;
      }
    }     

    return false;
  }

  public static Visitor doInputChecck(ActionForm form, 
				      HttpServletRequest request,
				      boolean useFullName,
				      boolean isFrom,
				      MessageResources resources,
				      Locale locale)
    throws Exception 
  {
    // first check whether the sign-in ID exists in the input
    // if so, check whether the sign-in ID is valid
    //    Invalid, set error code, return (INVALID_INPUT)
    //    Valid, get the visitor and compare its names if existing to
    //      see whether they match. Return correspondingly. (INPUT_NOTUNIQUE)
    // sign-in ID not present
    //    Search the user (INPUT_NOTUNIQUE)

    String fullName = (String) getP(form, ((isFrom) ? "fullNameFrom" : "fullNameTo"));
    fullName = (null != fullName) ? fullName.trim() : null;

    String firstName = (String) getP(form, ((isFrom) ? "firstNameFrom" : "firstNameTo"));
    firstName = (null != firstName) ? firstName.trim() : null;

    String lastName = (String) getP(form, ((isFrom) ? "lastNameFrom" : "lastNameTo"));
    lastName = (null != lastName) ? lastName.trim() : null;

    VisitorManager vm = new VisitorManager();

    String userAlias = (String) getP(form, ((isFrom) ? "userAliasFrom" : "userAliasTo"));

//System.out.println(">>>>>>>>>>isFrom:" + isFrom + " userAlias=" + userAlias + "  fullName=" + fullName + "  first=" + firstName + "  last=" + lastName);

    Visitor visitor = null;

    if (!isEmptyString(userAlias)) {
      userAlias = userAlias.trim();
      if (!vm.checkIfVisitorExists(userAlias)) {
	// no existant
      }
      else {
	// existant
	try {
	  visitor = vm.getVisitor(userAlias);

	  if (useFullName) {
	    // user only enter sign-in ID. Accept no question ask
	    if ((!isEmptyString(fullName)) &&
		(!fullName.equalsIgnoreCase(visitor.getStringValue("NAME")))) {
	      visitor = null;
	    }
	  }    
	  else {
	    // use FML name
	    String vFN = visitor.getStringValue("FIRST_NAME");
	    String vLN = visitor.getStringValue("LAST_NAME");
	    if ((!isEmptyString(firstName)) && 
		(isEmptyString(lastName)) && 
		((isEmptyString(vFN)) || 
		 (!firstName.equalsIgnoreCase(vFN)))) {
	      visitor = null;
	    }
	    else if (isEmptyString(firstName) && 
		     (!isEmptyString(lastName)) &&
		     ((isEmptyString(vLN)) || 
		      (!lastName.equalsIgnoreCase(vLN)))) {
	      visitor = null;
	    }
	    else if ((!isEmptyString(firstName)) && 
		     (!isEmptyString(lastName)) &&
		     ((!firstName.equalsIgnoreCase(vFN)) || 
		      (!lastName.equalsIgnoreCase(vLN)))) {
	      visitor = null;
	    }
	  } // useFullName ?
	}
	catch (Exception ex) {
	  // the userAlias may have been deleted
	  visitor = null;
	}
      } // sign-in ID existant
      if (null == visitor) {
	request.setAttribute(Globals.ERROR_KEY, ((isFrom) ? "bp.user.error.noMatchFrom" : "bp.user.error.noMatchTo"));
      }
    }
    else if ((!isEmptyString(firstName)) || (!isEmptyString(lastName)) ||
	     (!isEmptyString(fullName))) {
      // sign-in ID not present
      // do search
      UserSearch userSearch = new UserSearch();
      userSearch.setUseFullName(useFullName);
      if (useFullName) {
	userSearch.setFullName(fullName);
      }
      else {
	userSearch.setFirstName(firstName);
	userSearch.setLastName(lastName);
      }
      Visitor[] visitors = userSearch.search();
      int numOfVisitors = visitors == null ? 0 : visitors.length;
      if (1 == numOfVisitors) {
	visitor = visitors[0];
      }
      else {
	if (0 == numOfVisitors) {
	  request.setAttribute(Globals.ERROR_KEY, ((isFrom) ? "bp.user.error.noMatchFrom" : "bp.user.error.noMatchTo"));
	}
	else {
	  request.setAttribute(Globals.ERROR_KEY, ((isFrom) ? "bp.user.error.tooManyMatchesFrom" : "bp.user.error.tooManyMatchesTo"));
	}
      }
    }    
    else {
      request.setAttribute(Globals.ERROR_KEY, ((isFrom) ? "bp.user.error.missingFrom" : "bp.user.error.missingTo"));
    }
    return visitor;
  }

  public static boolean isEmptyString(String string) {
    return (string != null && string.trim().length() > 0) ? false : true;
  }


  public static Object getP(ActionForm form, String attr)
    throws java.lang.IllegalAccessException, 
    java.lang.reflect.InvocationTargetException,
    java.lang.NoSuchMethodException
  {
    return PropertyUtils.getSimpleProperty(form, attr);
  }

  public static void setP(ActionForm form, String attr, Object value)
    throws java.lang.IllegalAccessException, 
    java.lang.reflect.InvocationTargetException,
    java.lang.NoSuchMethodException
  {
    PropertyUtils.setSimpleProperty(form, attr, value);
  }

  public static void setP(ViewDynaBean view, String attr, Object value)
    throws java.lang.IllegalAccessException, 
    java.lang.reflect.InvocationTargetException,
    java.lang.NoSuchMethodException
  {
    PropertyUtils.setSimpleProperty(view, attr, value);
  }

  public static boolean isManagerForAnyProcessInstance(String alias, int serviceId) {
    try {
      if (dataManager == null) {
        dataManager = new DataManager();
      }
      StringBuffer query = new StringBuffer();
      query.append("SELECT ");
      query.append(TableConstants.OID);
      query.append(" FROM "); 
      query.append(TableConstants.BV_PROC_INSTANCE);
      query.append(" WHERE ");
      query.append(TableConstants.DELETED);
      query.append("=0 AND ");
      query.append(TableConstants.MANAGER);
      query.append("='");
      query.append(alias);
      query.append("'");
      if (serviceId >= 0) {
        query.append(" AND ");
        query.append(TableConstants.STORE_ID);
        query.append("=");
        query.append(serviceId);
      }
      ArrayList valueList = new ArrayList();
      TableData tbl = dataManager.getDataByBoundQuery(null, query.toString(), valueList);
      if (tbl != null && tbl.size() > 0) {
        return true;
      }
    }
    catch (Exception ex) {
      ;
    }
    return false;
  }

  public static boolean isManagerForAnyProcessModel(String alias, int serviceId) {
    try {
      if (dataManager == null) {
        dataManager = new DataManager();
      }
      StringBuffer query = new StringBuffer();
      query.append("SELECT ");
      query.append(TableConstants.OID);
      query.append(" FROM "); 
      query.append(TableConstants.BV_PROC_DEFINITION);
      query.append(" WHERE ");
      query.append(TableConstants.DELETED);
      query.append("=0 AND ");
      query.append(TableConstants.MANAGER_RESOLVED);
      query.append("=1 AND ");
      query.append(TableConstants.MANAGER);
      query.append("='");
      query.append(alias);
      query.append("'");
      if (serviceId >= 0) {
        query.append(" AND ");
        query.append(TableConstants.STORE_ID);
        query.append("=");
        query.append(serviceId);
      }
      ArrayList valueList = new ArrayList();
      TableData tbl = dataManager.getDataByBoundQuery(null, query.toString(), valueList);
      if (tbl != null && tbl.size() > 0) {
        return true;
      }
    }
    catch (Exception ex) {
      ;
    }
    return false;
  }

  public static void prepareExistingDelegationTimes(ActionForm form,
						    Delegation delegation)
    throws Exception
  {
    DateTime oldEnd = (DateTime) UserUtils.getP(form, "oldEnd");
    Date currentTime = new Date();
    if (! currentTime.before(oldEnd.getDate())) {
      //throw new Exception("cannot edit past delegations");
      throw new Exception("bp.user.delegation.error.editPastDelegation");
    }
    else {
      prepareNewEndTime(form, delegation);
    }
    
    // refresh the current time
    DateTime oldStart = (DateTime) UserUtils.getP(form, "oldStart");
    currentTime = new Date();
    if (! currentTime.before(oldStart.getDate())) {
      delegation.setStartTime(oldStart.getDate());
    }
    else {
      prepareNewStartTime(form, delegation);
    }
    
    if (delegation.getStartTime().after(delegation.getEndTime())) {
      //throw new Exception("start time cannot be after end time!");
      throw new Exception("bp.user.delegation.error.startTimeAfterEndTime");
    }
  }

  public static void prepareNewEndTime(ActionForm form, Delegation delegation)
    throws Exception
  {
    Date currentTime = new Date();
    
    String endRadio = (String) UserUtils.getP(form, "endRadio");
    if ("NOW".equals(endRadio)) {
      delegation.setEndTime(currentTime);
    } else {

      DateTime end = (DateTime) UserUtils.getP(form, "end");
      end.setInd(1);
      end.setHour(11);
      end.setMinute(59);
      
      if (end.getDate().before(currentTime)) {
	//throw new Exception("cannot set end date in the past!");
	throw new Exception("bp.user.delegation.error.endTimeInThePast");
      }
      else {
	delegation.setEndTime(end.getDate());
      }
    }
  }

  public static void prepareNewStartTime(ActionForm form,
					 Delegation delegation)
    throws Exception
  {
    Date currentTime = new Date();
    
    String startRadio = (String) UserUtils.getP(form, "startRadio");
    if ("NOW".equals(startRadio)) {      
      delegation.setStartTime(currentTime);
    } 
    else {
      DateTime start = (DateTime) UserUtils.getP(form, "start");
      DateTime current = new DateTime();
      current.setDateTime(currentTime);
      
      // if start day is today, set start time to now
      if (current.getMonth() == start.getMonth() &&
	  current.getDay() == start.getDay() &&
	  current.getYear() == start.getYear()) {
	
	start = current;
      }
      // otherwise set start time to 12:00 am
      else {
	
	start.setInd(0);
	start.setHour(0);
	start.setMinute(0);
      }
      
      if (start.getDate().before(currentTime)) {
	//throw new Exception("cannot set start date in the past!");
	throw new Exception("bp.user.delegation.error.startTimeInThePast");
      }
      else {
	delegation.setStartTime(start.getDate());
      }
    }
  }
  
  public static void prepareNewDelegationTimes(ActionForm form, 
					       Delegation delegation)
    throws Exception
  {
    prepareNewStartTime(form, delegation);
    prepareNewEndTime(form, delegation);
    
    if (delegation.getStartTime().after(delegation.getEndTime())) {
      //throw new Exception("start time cannot be after end time!");
      throw new Exception("bp.user.delegation.error.startTimeAfterEndTime");
    }
  }

  public static void doCheckTime(ActionForm form, HttpServletRequest request)
    throws Exception 
  {
    DelegationManager dm = new DelegationManager();
    Long delegationOid = (Long) UserUtils.getP(form, "delegationOid");
    Delegation delegation = null;
    
    if (delegationOid.longValue() == -1) {
      // create new delegation

      delegation = new Delegation();
      TransferBeanUtils.copyPropertiesToModel(delegation, form);
      prepareNewDelegationTimes(form, delegation);
    }
    else {
      // update existing delegation
      delegation = dm.getDelegation(delegationOid.longValue());
      TransferBeanUtils.copyPropertiesToModel(delegation, form);
      prepareExistingDelegationTimes(form, delegation);
    }
  }

  public static void prepareIsCurrentInViewBean(ActionForm form,
						HttpServletRequest request,
						ViewDynaBean view)
    throws Exception
  {
    
    Long delegationOid = (Long) UserUtils.getP(form, "delegationOid");
    
    if (delegationOid.longValue() != -1) {
      
      DateTime oldStart = (DateTime) UserUtils.getP(form, "oldStart");
      Date currentTime = new Date();

      boolean isCurrent = (currentTime.before(oldStart.getDate())) ? false : true;
      UserUtils.setP(view, "isCurrent", new Boolean(isCurrent));
      
      if (isCurrent) {
	UserUtils.setP(form, "start", oldStart);
	if (oldStart.getMinute() != 0 ||
	    oldStart.getHour() != 0 || 
	    oldStart.getInd() != 0) {
	  UserUtils.setP(view, "showStartTime", Boolean.TRUE);
	}
	else {
	  UserUtils.setP(view, "showStartTime", Boolean.FALSE);
	}
      }
    }
  }

  public static void validateCanEditDelegation(ActionForm form)
    throws Exception
  {
    // because it takes time to edit a delegation, we need to continously
    // validate whether we can still edit it (i.e., the end time has not
    // yet elapsed).
    
    Long delegationOid = (Long) getP(form, "delegationOid");
    
    if (delegationOid.longValue() != -1) {
      DateTime oldEnd = (DateTime) getP(form, "oldEnd");
      Date currentTime = new Date();
      if (! currentTime.before(oldEnd.getDate())) {
	//TODO:  use different exception
	throw new Exception("cannot edit past delegations");
      }
    }
  } 

  public static final String TASK_DELEGATION_TYPE = "task";

  /**
   * Removes all the delegation from and to a user
   */
  public static void removeDelegations(long userId) throws Exception {

    Date startTime = null;
    Date endTime = null;
    DelegationManager dm = new DelegationManager();
 
    List taskDelegatorList = dm.getDelegations(userId,
					       -1,
					       startTime,
					       endTime,
					       TASK_DELEGATION_TYPE);
    removeDelegations(taskDelegatorList);
    List taskDelegateList = dm.getDelegations(-1,
					      userId,
					      startTime,
					      endTime,
					      TASK_DELEGATION_TYPE);
    removeDelegations(taskDelegateList);
      
    List pmDelegatorList = dm.getDelegations(userId,
					     -1,
					     startTime,
					     endTime,
					     SelfServiceProcess.DELEGATION_TYPE);
    removeDelegations(pmDelegatorList);

    List pmDelegateList = dm.getDelegations(-1,
					    userId,
					    startTime,
					    endTime,
					    SelfServiceProcess.DELEGATION_TYPE);
    removeDelegations(pmDelegateList);
  }
  
  public static void removeDelegations(List delegationList) throws Exception {
    DelegationManager dm = new DelegationManager();
    for (Iterator it = delegationList.iterator(); it.hasNext();) {
      Delegation de = (Delegation) it.next();
      dm.removeDelegation(de.getOid());
    }
  }
}
